<?php
/**
 * @author jonathan <jo.link.noir@gmail.com>
 * @version 1.0: jo_link_noir
 * @date 14-04-2010
 */
class U1_Move_Attack extends SW_Move
{
	/** @var string*/
	public $orderName = 'attack';


	/**
	 * @param int $endPoint
	 * @param int $time = null
	 */
	public function __construct($endPoint, $time = null)
	{
		parent::__construct($endPoint, null, $time);
	}

	/**
	 * @param U1_Units $army
	 * @param U1_Planet $planet
	 * @return U1_Removable_Resource
	 */
	protected function getResourcePilier(U1_Units $units, U1_Planet $planet)
	{
		$freight = $units->getStat(U1_Unit::STAT_FREIGHT);
		$resource = $planet->getInstance(U1_Resource::INDEX, $this->endPoint);
		$resourceQuantity = $resource->getQuantities($this->time);
		$protectedResources = $planet->getInstance(U1_Building::INDEX, $this->endPoint)
			->getProtectedResources();

		$pilier = array();
		foreach ($resourceQuantity as $k => $v)
		{
			$v -= $protectedResources[$k];
			if (0 >= $v)
				unset($resourceQuantity[$k]);
			else
				$pilier[$k] = 0;
		}

		//calcul des ressources volées
		//diviseur de ressource (quantité à stocké)
		$div = count($resourceQuantity);
		while ($div && 0 < $freight)
		{
			$getResource = $freight / $div;
			foreach ($resourceQuantity as $k => &$quantity)
			{
				if (0 >= $quantity)
				{
					unset($resourceQuantity[$k]);
					--$div;
				}
				else if ($getResource < $quantity)
				{
					$freight -= $getResource;
					$pilier[$k] += $getResource;
					$quantity -= $getResource;
				}
				else
				{
					$freight -= $quantity;
					$pilier[$k] += $quantity;
					unset($resourceQuantity[$k]);
					--$div;
				}
			}
		}

		foreach ($pilier as $k => &$v)
		{
			if (0 >= $v)
				unset($pilier[$k]);
			else
				$v = (int)$v;
		}

		$resource->decrease($pilier, $this->time);
		return new U1_Removable_Resource($pilier);
	}

	/**
	 * Récupère et finit la construction de la structure (finishCompletedLists) de la planète désigné par $point
	 * @param U1_Planet $planet
	 * @param mixed $nameStruct
	 * @param int point
	 * @return SW_Structure
	 */
	protected function _getStructureAndFinishCompletLists(U1_Planet $planet, $nameStruct, $point)
	{
		$struct = $planet->getInstance($nameStruct, $point);
		$struct->finishCompletedLists($this->time);
		return $struct;
	}

	/**
	 * Récupère et finit la construction de la structure de $this->endPoint (_getStructureAndFinishCompletLists)
	 * @param U1_Planet $planet
	 * @param mixed $nameStruct
	 * @return SW_Structure
	 */
	protected function getStructureEndPointAndFinishCompletLists(U1_Planet $planet, $nameStruct)
	{
		return $this->_getStructureAndFinishCompletLists($planet, $nameStruct, $this->endPoint);
	}

	/**
	 * Récupère et finit la construction de la structure de $this->startPoint (_getStructureAndFinishCompletLists)
	 * @param U1_Planet $planet
	 * @param mixed $nameStruct
	 * @return SW_Structure
	 */
	protected function getStructureStartPointAndFinishCompletLists(U1_Planet $planet, $nameStruct)
	{
		return $this->_getStructureAndFinishCompletLists($planet, $nameStruct, $this->startPoint);
	}

	/**
	 * @param string $message
	 * @param bool $sendMessagePlayerTarget = true : si false aucun message n'est envoyer à la planète attaquer
	 */
	protected function sendMessage($message, $sendMessagePlayerTarget = true)
	{
		if ($sendMessagePlayerTarget)
			U1_DAO_Message::send($this->player_target, $message, U1_Display_Move::getSujetMove($this->orderName, true), false);
		U1_DAO_Message::send($this->player_sends, $message, U1_Display_Move::getSujetMove($this->orderName, false), false);
	}

	/**
	 * Méthode à déclencher quand l'armé est arrivé à destination
	 * @param SW_Units $army
	 * TODO méthode d'envoie de message dans la classe (déplacé celui dans U1_Display_Move)
	 */
	public function arrive(SW_Units $units)
	{
		$baseSend = true;
		$resource = null;
		//récupère la planète cible
		if ($planetEnd = U1_Planet::factoryContainsPlanetId($this->endPoint))
		{
			if ('spy' === $this->orderName)
			{
				$probe = $units->offsetGet(4);
				$count = $probe->getCount();

				//récupère l'objet des recherches et termine les constructions
				$researchEnd = $this->getStructureEndPointAndFinishCompletLists($planetEnd, U1_Research::INDEX);
				//récupère l'objet des batiments et termine les constructions
				$buildingEnd = $this->getStructureEndPointAndFinishCompletLists($planetEnd, U1_Building::INDEX);

				$detect = sqrt(1
					+ $researchEnd->getLevelSumGroup(17) //technique d'espionnage
					+ (1 + $researchEnd->getLevelSumGroup(18) * 5) //détection de vaisseaux
					* (
						$buildingEnd->getLevelSumGroup(8) //radar
						+ $buildingEnd->getLevelSumGroup(9) * 3 //radar avancée
					)
				);
				$planetStart = U1_Planet::factoryContainsPlanetId($this->startPoint);
				//l'efficacité des sondes (proportionnel au nombre) est diminué par leurs nombres (moins)
				$spy = floor(sqrt($count * (1 +
						$this->getStructureStartPointAndFinishCompletLists($planetStart, U1_Research::INDEX)->getLevelSumGroup(17)
					)) * 400 / ($count + $detect)
				);

				$planetElementStart = $planetStart->get($this->startPoint);
				$planetElementEnd = $planetEnd->get($this->endPoint);

				if ($spy > 0)
				{
					$strucSpy = array(
						U1_Resource::INDEX =>
							$planetEnd->getInstance(U1_Resource::INDEX, $this->endPoint)
								->getQuantities($this->time),
						U1_Army::INDEX =>
							$this->getStructureEndPointAndFinishCompletLists($planetEnd, U1_Army::INDEX)
								->getLevelSumGroups(),
						U1_Building::INDEX => $buildingEnd->getLevelSumGroups(),
						U1_Research::INDEX => $researchEnd->getLevelSumGroups()
					);
					//supprime les valeurs vide (=0) et compte le nombre d'élements
					$countStruct = 0;
					foreach ($strucSpy as &$struc)
					{
						SF_Process_Array::removeEmptyValue($struc);
						$countStruct += count($struc);
					}

					$dead = 0;
					//tout n'est pas espionnable
					if ($spy < $countStruct)
					{
						//sonde détruite
						$dead = floor($count * (($countStruct - $spy) / $countStruct));
						if ($count <= $dead)
							$baseSend = false;
						else
							$units->decreaseUnit(4, $dead);

						$keys = array();
						//recupère chaque clée
						SF_Process_Array::addKeys($keys, $strucSpy, 2);
						$strucSpy = SF_Process_Array::getRecursiveValues(
							$strucSpy,
							//recupère les valeurs des clés précèdement enregistrée
							SF_Process_Array::getValues($keys, array_rand($keys, $spy))
						);
					}

					$this->sendMessage(U1_Display_Move::getReportSpy($planetElementStart, $planetElementEnd, intval($spy/$countStruct*100), $strucSpy, $dead), $dead > 0);
				}
				else
				{
					$this->sendMessage(U1_Display_Move::getReportSpy($planetElementStart, $planetElementEnd, 0, array(), $count));
					$baseSend = false;
				}
			}
			else
			{
				//récupère l'objet des armes et termine les constructions
				$armyEnd = $this->getStructureEndPointAndFinishCompletLists($planetEnd, U1_Army::INDEX);
				$unitsEnd = $armyEnd->getUnits(true);

				//si c'est vide, c'est une victoire
				if ($unitsEnd->isEmpty())
				{
					//pillage de ressources
					$resource = $this->getResourcePilier($units, $planetEnd);

					$this->sendMessage(U1_Display_Move::getReportAttack(U1_DAO_Planet::selectId($this->startPoint), $planetEnd->get($this->endPoint), U1_Display_Army::getMessageConfrontationResult(false, true), $resource));
				}
				else
				{
					//récupère l'objet des recherches et termine les constructions
					$researchEnd = $this->getStructureEndPointAndFinishCompletLists($planetEnd, U1_Research::INDEX);
					//initialisent les unités
					$armyEnd->initStats($unitsEnd, $researchEnd);

					$countsUnitsEnd = $unitsEnd->toArray();
					$message = U1_Army_Attack::confrontation($units, $unitsEnd);

					//$countsUnitsEnd va contenir les pertes
					$countsUnitsEndAfter = $unitsEnd->toArray();
					foreach ($countsUnitsEnd as $k => &$v)
					{
						if (isset($countsUnitsEndAfter[$k]))
						{
							if ($countsUnitsEndAfter[$k] == $v)
								unset($countsUnitsEnd[$k]);
							else
								$v -= $countsUnitsEndAfter[$k];
						}
					}

					//actualisé la bdd. Les objets Army_Element seront automatiquement actualisés
					U1_Army_DAO::getFactory()->decrease($this->player_target, $countsUnitsEnd);

					if ($units->isEmpty())
						$baseSend = false;
					else
					{
						if ($unitsEnd->isEmpty())
							//pillage de ressources
							$resource = $this->getResourcePilier($units, $planetEnd);
					}

					$this->sendMessage(U1_Display_Move::getReportAttack(U1_DAO_Planet::selectId($this->startPoint), $planetEnd->get($this->endPoint), $message, $resource));
				}
			}
		}

		if ($baseSend)
			U1_DAO_Move::baseSend($units, $this, $resource);
		else
			U1_DAO_Move::deleteId($this->id);
	}
}